Skip to content

feat(usage): enforce one free account per install (fair-use policy) - #159

Closed
Fahad090NP wants to merge 4 commits into
ltmoerdani:mainfrom
Fahad090NP:feat/single-free-account-policy
Closed

feat(usage): enforce one free account per install (fair-use policy)#159
Fahad090NP wants to merge 4 commits into
ltmoerdani:mainfrom
Fahad090NP:feat/single-free-account-policy

Conversation

@Fahad090NP

Copy link
Copy Markdown
Contributor

📝 What does this change?

A single-free-account fair-use policy so one person can't harvest OpenCode's free quota by registering multiple free accounts (inference is expensive for everyone).

The rule: exactly one free account is allowed per install. The moment 2+ free accounts are detected, free-model usage is blocked with a clear, actionable error. Paid usage is never blocked — multiple paid accounts are fine. Delete down to 1 free account (or make the extra one paid) and free models work again.

How an account is classified (per API-key fingerprint):

  • Free = a profile that uses a free model (*-free / big-pickle).
  • Paid = has an active Go subscription (checked via the official /zen/go/v1/usage endpoint) or successfully uses a paid model → exempt from the free limit.
  • Subscription lapsed? → Go accounts are re-classified within ~1 min via the usage sync; Zen accounts within 24h of the last paid use → they count as free again and the limit applies. Renewing re-confirms paid.

Coverage: new profiles and existing profiles (re-evaluated on every request), the Agents window, and vision-proxy requests. The block fires before the request is sent, so no quota is spent.

Code structure (4 commits, pure-logic separation):

  1. Pure policy module src/usage/freeAccountPolicy.ts + storage wrapper + 11 unit tests (src/test/freeAccountPolicy.test.ts) covering: single free allowed, 2nd free blocks, paid exemption, lapsed-sub re-classification, TTL renewal, delete-account recovery, round-trip persistence.
  2. Provider enforcement in provideLanguageModelChatResponse (+ paid confirmation on successful paid-model usage).
  3. Go subscription status wired from the usage sync into the policy.
  4. Delete Profile cleans the policy; the Usage quick-pick warns when free usage is blocked; Diagnostics now shows a "Free-Account Policy" section (free/paid fingerprints + blocked state) so the policy is transparent.

🧪 How did you test it?

  • npm run lint — all 7 steps green, incl. the 11 new policy unit tests.
  • Full gate re-run after each of the 4 commits.
  • Logic verified by scenario tests (can't test live with 2 free accounts myself — the Diagnostics section lets anyone confirm the state).

✅ Checklist

  • npm run compile passes
  • npm run lint passes
  • npm test passes
  • npm run package produces a VSIX (not run — code + tests only)
  • I tested it works (live multi-account verification still recommended)
  • I updated docs/CHANGELOG if needed (left for release-time per repo convention)

⚠️ Honest limitations (please read)

  1. Per-install deterrent, not identity proof. Two keys can't be proven to belong to one person; a determined user could bypass it on another machine or via the OpenCode CLI. It stops casual multi-account harvesting.
  2. An account that has never used anything yet can't be pre-classified — it's counted the moment it first uses a free model.
  3. Zen paid accounts have up to 24h grace after their last paid use (there's no subscription-check endpoint for Zen; Go has one, hence ~1 min).

PEACE BE UPON YOU 🤲

@Fahad090NP

Copy link
Copy Markdown
Contributor Author

If I ever had a wish for any of my PR to be approved, then this is the one. Not because I am jealous of the people who are using this trick to scam OpenCode and I am not, but because both Islam and then the ethics forbids such acts strictly. It was always be a difficult thing to let others think what I actually want to say.

Fi Amanillah.

@Fahad090NP

Copy link
Copy Markdown
Contributor Author

I do not want to hear anything other than any bug or inconsistency in my code. Either Merge, tell me what to improve, or Reject it.

@ltmoerdani

Copy link
Copy Markdown
Owner

@Fahad090NP, thanks for this. I read through it twice and I'll give you the straight answer you asked for.

The code itself is clean. The pure module split, the 11 tests, the honest limitations section, it's better put together than most PRs I get here. I also understand the motivation and I respect it. But I'm going to reject this one, and you asked for bugs and inconsistencies, so here they are.

The first one is serious: it contradicts a feature we shipped about five weeks ago. Issue #63 and PR #75 added multi-account profile support because people have legitimate reasons to hold multiple keys. A work account and a personal one. Teams where every developer runs their own Go subscription. One account with keys spread across machines. Your policy counts accounts by key fingerprint, so those users walk straight into the block.

  • Rotate an API key on the same account and you get a new fingerprint. One person, one account, suddenly two "free accounts".
  • A Go subscription lapsing re-classifies that profile as free within about a minute of the next usage sync. Someone who stopped paying that day loses their free-model fallback at the exact moment they'd want it.

There's also a state leak. Remove a BYOK entry through the VS Code Manage panel instead of our Delete Profile command and the fingerprint stays in the policy storage. Nothing clears it unless they happen to run Delete Profile on that exact profile. Blocked, with no obvious way out.

I could ask you to fix the fingerprint handling. Even fixed, the deeper problem stays: the rule itself doesn't exist. I checked OpenCode's Terms of Use and the Zen docs before writing this. There is no one-free-account-per-person policy anywhere. The free models are a limited-time feedback program, and the ToS explicitly leaves violation decisions to OpenCode. Shipping this means enforcing a rule we invented on their behalf, and the error message reads "OpenCode free-account limit reached". Someone hits that at midnight, reads it, and walks away thinking OpenCode blocked them when it was our extension. That turns into bug reports I can't do anything about.

And since the rule is invented, check who it actually catches. Anyone harvesting quota can use the CLI, another machine, or a separate VS Code profile, since globalState doesn't cross profiles. The ones who hit the wall are the legitimate multi-account users above. If OpenCode wants fair-use enforcement, they have the identity and billing data server-side to do it right. That's where this belongs, not in a client.

If you want to keep pushing the idea, the version I'd merge is a non-blocking warning: a status-bar hint when multiple free fingerprints are detected, no request blocking. Tell the user, let them decide.

One more thing, about the comment you left below. I read it, and I want to answer part of it. This PR doesn't get merged, but the intention behind it, protecting something you believe is right and refusing to look away from what you see as wrong, is already recorded by Allah. May He accept it as a good deed for you. That intention counts for more than the code.

Hope to see more PRs from you on other things. Jazakallah Khair.

@ltmoerdani ltmoerdani closed this Aug 17, 2026
@Fahad090NP
Fahad090NP deleted the feat/single-free-account-policy branch August 17, 2026 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants